package com.zenith.gateway.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zenith.common.constant.CommonConstant;
import com.zenith.common.result.Result;
import com.zenith.common.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 动态权限检查过滤器
 * 实现基于Redis的动态权限控制
 */
@Slf4j
@Component
public class DynamicPermissionFilter implements GlobalFilter, Ordered {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    // 权限缓存过期时间（分钟）
    private static final long PERMISSION_CACHE_EXPIRE = 10;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        String method = request.getMethod().name();
        
        // 跳过白名单路径
        if (isWhitelist(path)) {
            return chain.filter(exchange);
        }
        
        // 获取token
        String token = getToken(request);
        if (!StringUtils.hasText(token)) {
            return chain.filter(exchange);
        }
        
        // 验证token
        if (!JwtUtil.validateToken(token)) {
            return chain.filter(exchange);
        }
        
        // 获取用户信息
        Long userId = JwtUtil.getUserId(token);
        String userRole = JwtUtil.getUserRole(token);
        
        // 检查是否需要动态权限验证
        if (needDynamicPermissionCheck(path, method)) {
            // 检查用户是否有权限
            if (!hasDynamicPermission(userId, path, method)) {
                log.warn("用户 {} 缺少访问 {} 的权限", userId, path);
                return forbidden(exchange, "权限不足");
            }
        }
        
        return chain.filter(exchange);
    }
    
    @Override
    public int getOrder() {
        // 在认证过滤器之后执行
        return 1;
    }
    
    /**
     * 判断是否为白名单路径
     */
    private boolean isWhitelist(String path) {
        // 简化处理，实际应用中应该从配置中读取白名单
        return path.contains("/auth/") || path.contains("/swagger") || path.contains("/doc");
    }
    
    /**
     * 获取请求头中的token
     */
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(CommonConstant.TOKEN_HEADER);
        if (StringUtils.hasText(token) && token.startsWith(CommonConstant.TOKEN_PREFIX)) {
            return token.substring(CommonConstant.TOKEN_PREFIX.length());
        }
        return null;
    }
    
    /**
     * 判断是否需要动态权限检查
     */
    private boolean needDynamicPermissionCheck(String path, String method) {
        // 对于管理接口和敏感操作需要动态权限检查
        return path.startsWith("/api/admin/") || 
               path.contains("/delete") || 
               path.contains("/update") ||
               "POST".equals(method) || 
               "PUT".equals(method) || 
               "DELETE".equals(method);
    }
    
    /**
     * 检查用户是否有动态权限
     */
    private boolean hasDynamicPermission(Long userId, String path, String method) {
        // 构造权限键
        String permissionKey = "user:permissions:" + userId;
        String permission = path + ":" + method;
        
        try {
            // 检查缓存中是否有权限信息
            Boolean hasKey = redisTemplate.hasKey(permissionKey);
            if (hasKey != null && hasKey) {
                // 从缓存中获取权限集合
                Set<String> permissions = redisTemplate.opsForSet().members(permissionKey);
                if (permissions != null && permissions.contains(permission)) {
                    return true;
                }
            }
            
            // 缓存中没有权限信息，需要从数据库加载
            // 这里应该调用用户服务获取用户权限
            // 简化处理，直接从数据库查询并缓存
            Set<String> permissions = loadUserPermissionsFromDB(userId);
            
            if (permissions != null && !permissions.isEmpty()) {
                // 将权限信息缓存到Redis
                redisTemplate.opsForSet().add(permissionKey, permissions.toArray(new String[0]));
                redisTemplate.expire(permissionKey, PERMISSION_CACHE_EXPIRE, TimeUnit.MINUTES);
                
                // 检查是否有对应权限
                return permissions.contains(permission);
            }
        } catch (Exception e) {
            log.error("检查用户权限时发生错误", e);
        }
        
        return false;
    }
    
    /**
     * 从数据库加载用户权限
     */
    private Set<String> loadUserPermissionsFromDB(Long userId) {
        // 在实际应用中，这里应该调用用户服务获取用户权限
        // 简化处理，返回空集合
        return Set.of();
    }
    
    /**
     * 返回禁止访问响应
     */
    private Mono<Void> forbidden(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        
        Result<?> result = Result.forbidden(message);
        byte[] bytes;
        try {
            bytes = objectMapper.writeValueAsString(result).getBytes(StandardCharsets.UTF_8);
        } catch (Exception e) {
            bytes = "{\"code\":403,\"message\":\"禁止访问\",\"data\":null}".getBytes(StandardCharsets.UTF_8);
        }
        
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(buffer));
    }
}
